Skip to content

refactor (backup) : conditionally copy registry auth secret from operator namespace to workspace namespace for backup/restore#1618

Open
rohanKanojia wants to merge 3 commits intodevfile:mainfrom
rohankanojia-forks:pr/secret-fallback-option
Open

refactor (backup) : conditionally copy registry auth secret from operator namespace to workspace namespace for backup/restore#1618
rohanKanojia wants to merge 3 commits intodevfile:mainfrom
rohankanojia-forks:pr/secret-fallback-option

Conversation

@rohanKanojia
Copy link
Copy Markdown
Member

@rohanKanojia rohanKanojia commented May 5, 2026

What does this PR do?

Only copy the registry auth secret from the operator namespace to the workspace namespace when it is configured inside DevWorkspaceOperatorConfig. Always give precedence to the user-defined secret named devworkspace-backup-registry-auth in the workspace namespace; do not modify it when it's present while copying.

What issues does this PR fix or reference?

https://redhat.atlassian.net/browse/CRW-10758

Is it tested? How?

Scenario 1: AuthSecret Not Configured (Secure by Default)

Expected: Operator will NOT copy secrets. User must provide their own.

  1. Configure DWOC without AuthSecret:
kubectl apply -f - <<EOF
apiVersion: controller.devfile.io/v1alpha1
kind: DevWorkspaceOperatorConfig
metadata:
  name: devworkspace-operator-config
  namespace: openshift-operators
config:
  workspace:
    backupCronJob:
      enable: true
      schedule: "* * * * *"
      registry:
        path: "quay.io/rokumar"
        # authSecret is NOT set - secure by default
EOF
  1. Create test namespace
oc new-project test-workspace-no-auth
  1. Create a DevWorkspace
kubectl apply -f - <<EOF
apiVersion: workspace.devfile.io/v1alpha2
kind: DevWorkspace
metadata:
  name: test-workspace-no-copy
  namespace: test-workspace-no-auth
spec:
  started: true
  template:
    projects:
      - name: web-nodejs-sample
        git:
          remotes:
            origin: "https://github.com/che-samples/web-nodejs-sample.git"
    components:
      - name: dev
        container:
          image: quay.io/devfile/universal-developer-image:latest
          memoryLimit: 512Mi
EOF
  1. Wait for workspace to become running, then stop it:
kubectl patch devworkspace test-workspace-no-copy -n test-workspace-no-auth \
  --type merge -p '{"spec":{"started":false}}'
  1. Expected Result: Check operator logs - should show error about missing AuthSecret:
oc logs -l app.kubernetes.io/name=devworkspace-controller -n openshift-operators -f

Expected log:

{"level":"info","ts":"2026-05-11T07:22:00Z","logger":"controllers.BackupCronJob","msg":"Registry auth secret not found in workspace namespace and not configured in operator config. Proceeding without authentication. Ensure your registry allows anonymous access or configure authentication if needed.","secretName":"devworkspace-backup-registry-auth","namespace":"test-workspace-no-auth","registry":"quay.io/rokumar"}
  1. Verify no secret was created:
kubectl get secret devworkspace-backup-registry-auth -n test-workspace-no-auth
# Expected: Error from server (NotFound)
Scenario 2: AuthSecret Configured - Operator Copies Secret

Expected: Operator copies secret from operator namespace to workspace namespace.

  1. Create operator secret first:
kubectl create secret docker-registry quay-push-secret \
  --docker-server=quay.io \
  --docker-username=<your-username> \
  --docker-password=<your-token> \
  -n openshift-operators

kubectl label secret quay-push-secret controller.devfile.io/watch-secret=true -n openshift-operators
  1. Configure DWOC with AuthSecret:
kubectl apply -f - <<EOF
apiVersion: controller.devfile.io/v1alpha1
kind: DevWorkspaceOperatorConfig
metadata:
  name: devworkspace-operator-config
  namespace: openshift-operators
config:
  workspace:
    backupCronJob:
      enable: true
      schedule: "* * * * *"
      registry:
        path: "quay.io/rokumar"
        authSecret: "quay-push-secret"  # Configured - will enable copying
EOF
  1. Create test namespace
oc new-project test-workspace-copy
  1. Create a DevWorkspace
kubectl apply -f - <<EOF
apiVersion: workspace.devfile.io/v1alpha2
kind: DevWorkspace
metadata:
  name: test-workspace-with-copy
  namespace: test-workspace-copy
spec:
  started: true
  template:
    projects:
      - name: web-nodejs-sample
        git:
          remotes:
            origin: "https://github.com/che-samples/web-nodejs-sample.git"
    components:
      - name: dev
        container:
          image: quay.io/devfile/universal-developer-image:latest
EOF
  1. Wait for workspace to become running, then stop it:
kubectl patch devworkspace test-workspace-with-copy -n test-workspace-copy \
  --type merge -p '{"spec":{"started":false}}'
  1. Expected Result: Verify secret was copied automatically:
kubectl get secret devworkspace-backup-registry-auth -n test-workspace-copy
# Expected: ✅ Secret exists

# Verify it has the controller.devfile.io/watch-secret label
kubectl get secret devworkspace-backup-registry-auth -n test-workspace-copy -o jsonpath='{.metadata.labels}'
Scenario 3: User-Provided Secret NEVER gets Overwritten

Expected: User's secret is respected and NEVER overwritten, even if operator has different credentials.

  1. Create operator secret:
kubectl create secret docker-registry quay-push-secret \
  --docker-server=quay.io \
  --docker-username=operator-user \
  --docker-password=operator-token \
  -n openshift-operators

kubectl label secret quay-push-secret controller.devfile.io/watch-secret=true -n openshift-operators
  1. Configure DWOC:
kubectl apply -f - <<EOF
apiVersion: controller.devfile.io/v1alpha1
kind: DevWorkspaceOperatorConfig
metadata:
  name: devworkspace-operator-config
  namespace: openshift-operators
config:
  workspace:
    backupCronJob:
      enable: true
      schedule: "* * * * *"
      registry:
        path: "quay.io/rokumar"
        authSecret: "quay-push-secret"
EOF
  1. Create test namespace
oc new-project test-workspace-user-secret
  1. IMPORTANT: User manually creates secret BEFORE workspace (with different credentials):
kubectl create secret docker-registry devworkspace-backup-registry-auth \
  --docker-server=quay.io \
  --docker-username=user-scoped \
  --docker-password=user-token \
  -n test-workspace-user-secret

kubectl label secret devworkspace-backup-registry-auth \
  controller.devfile.io/watch-secret=true \
  -n test-workspace-user-secret

# Note the resourceVersion
echo "Original resourceVersion:"
kubectl get secret devworkspace-backup-registry-auth -n test-workspace-user-secret -o jsonpath='{.metadata.resourceVersion}'
echo
  1. Create a DevWorkspace
kubectl apply -f - <<EOF
apiVersion: workspace.devfile.io/v1alpha2
kind: DevWorkspace
metadata:
  name: test-workspace-user-secret
  namespace: test-workspace-user-secret
spec:
  started: true
  template:
    projects:
      - name: web-nodejs-sample
        git:
          remotes:
            origin: "https://github.com/che-samples/web-nodejs-sample.git"
    components:
      - name: dev
        container:
          image: quay.io/devfile/universal-developer-image:latest
EOF
  1. Wait for workspace to become running, then stop it:
kubectl patch devworkspace test-workspace-user-secret -n test-workspace-user-secret \
  --type merge -p '{"spec":{"started":false}}'
  1. Critical Test: Verify secret was NOT overwritten:
echo "Current resourceVersion:"
kubectl get secret devworkspace-backup-registry-auth -n test-workspace-user-secret -o jsonpath='{.metadata.resourceVersion}'
echo

# ResourceVersion should be EXACTLY THE SAME as step 4
Scenario 4: AuthSecret Configured but Operator Secret Missing

Expected: Operator logs error that it cannot find the configured secret in operator namespace.

  1. Configure DWOC with non-existent secret:
kubectl apply -f - <<EOF
apiVersion: controller.devfile.io/v1alpha1
kind: DevWorkspaceOperatorConfig
metadata:
  name: devworkspace-operator-config
  namespace: openshift-operators
config:
  workspace:
    backupCronJob:
      enable: true
      schedule: "* * * * *"
      registry:
        path: "quay.io/rokumar"
        authSecret: "missing-secret"  # This secret doesn't exist
EOF
  1. Create test namespace
oc new-project test-workspace-missing-secret
  1. Ensure no operator secret exists:
kubectl delete secret missing-secret -n openshift-operators --ignore-not-found
  1. Create a DevWorkspace
kubectl apply -f - <<EOF
apiVersion: workspace.devfile.io/v1alpha2
kind: DevWorkspace
metadata:
  name: test-workspace-missing
  namespace: test-workspace-missing-secret
spec:
  started: true
  template:
    projects:
      - name: web-nodejs-sample
        git:
          remotes:
            origin: "https://github.com/che-samples/web-nodejs-sample.git"
    components:
      - name: dev
        container:
          image: quay.io/devfile/universal-developer-image:latest
EOF
  1. Wait for workspace to become running, then stop it:
kubectl patch devworkspace test-workspace-missing -n test-workspace-missing-secret \
  --type merge -p '{"spec":{"started":false}}'
  1. Expected Result: Check operator logs for error:
oc logs -l app.kubernetes.io/name=devworkspace-controller -n openshift-operators -f

Expected log:

{"level":"error","ts":"2026-05-08T14:28:00Z","logger":"controllers.BackupCronJob","msg":"Failed to get registry auth secret for backup job","secretName":"missing-secret","namespace":"openshift-operators","error":"Secret \"missing-secret\" not found","stacktrace":"github.com/devfile/devworkspace-operator/pkg/secrets.HandleRegistryAuthSecret\n\t/devworkspace-operator/pkg/secrets/backup.go:88\ngithub.com/devfile/devworkspace-operator/controllers/backupcronjob.(*BackupCronJobReconciler).createBackupJob\n\t/devworkspace-operator/controllers/backupcronjob/backupcronjob_controller.go:348\ngithub.com/devfile/devworkspace-operator/controllers/backupcronjob.(*BackupCronJobReconciler).executeBackupSync\n\t/devworkspace-operator/controllers/backupcronjob/backupcronjob_controller.go:253\ngithub.com/devfile/devworkspace-operator/controllers/backupcronjob.(*BackupCronJobReconciler).startCron.func1\n\t/devworkspace-operator/controllers/backupcronjob/backupcronjob_controller.go:194\ngithub.com/robfig/cron/v3.FuncJob.Run\n\t/devworkspace-operator/vendor/github.com/robfig/cron/v3/cron.go:131\ngithub.com/robfig/cron/v3.(*Cron).startJob.func1\n\t/devworkspace-operator/vendor/github.com/robfig/cron/v3/cron.go:307"}
  1. Verify no secret was created in workspace namespace:
kubectl get secret devworkspace-backup-registry-auth -n test-workspace-missing-secret
# Expected: Error from server (NotFound)
Scenario 5: OpenShift Internal Registry - No Auth Required

Expected: Backup works without authentication when using OpenShift internal registry service URL.

  1. Configure DWOC for OpenShift internal registry (no AuthSecret):
kubectl apply -f - <<EOF
apiVersion: controller.devfile.io/v1alpha1
kind: DevWorkspaceOperatorConfig
metadata:
  name: devworkspace-operator-config
  namespace: openshift-operators
config:
  workspace:
    backupCronJob:
      enable: true
      schedule: "* * * * *"
      registry:
        path: "image-registry.openshift-image-registry.svc:5000"
        # authSecret is NOT set - internal registry doesn't require it
EOF
  1. Create test namespace
oc new-project test-workspace-internal-registry
  1. Create a DevWorkspace
kubectl apply -f - <<EOF
apiVersion: workspace.devfile.io/v1alpha2
kind: DevWorkspace
metadata:
  name: test-workspace-internal
  namespace: test-workspace-internal-registry
spec:
  started: true
  template:
    projects:
      - name: web-nodejs-sample
        git:
          remotes:
            origin: "https://github.com/che-samples/web-nodejs-sample.git"
    components:
      - name: dev
        container:
          image: quay.io/devfile/universal-developer-image:latest
          memoryLimit: 512Mi
EOF
  1. Wait for workspace to become running, then stop it:
kubectl patch devworkspace test-workspace-internal -n test-workspace-internal-registry \
  --type merge -p '{"spec":{"started":false}}'
  1. Expected Result: Check operator logs - should show info message about proceeding without auth:
oc logs -l app.kubernetes.io/name=devworkspace-controller -n openshift-operators -f

Expected log:

{"level":"info","msg":"Registry auth secret not found in workspace namespace and not configured in operator config. Proceeding without authentication. Ensure your registry allows anonymous access or configure authentication if needed.","secretName":"devworkspace-backup-registry-auth","namespace":"test-workspace-internal-registry","registry":"image-registry.openshift-image-registry.svc:5000/backups"}
  1. Verify backup job was created and succeeded:
kubectl get jobs -n test-workspace-internal-registry -l controller.devfile.io/devworkspace_name=test-workspace-internal

# Check job status
kubectl get jobs -n test-workspace-internal-registry -l controller.devfile.io/devworkspace_name=test-workspace-internal -o jsonpath='{.items[0].status.succeeded}'
# Expected: 1 (job succeeded)
  1. Verify backup was pushed to internal registry:
# The backup image should exist in the OpenShift internal registry
oc get imagestream -n backups test-workspace-internal
Scenario 6: Public/Anonymous Registry - No Auth Required

Expected: Backup works without authentication for registries that allow anonymous push (rare but valid use case).

  1. Configure DWOC for a hypothetical public registry:
kubectl apply -f - <<EOF
apiVersion: controller.devfile.io/v1alpha1
kind: DevWorkspaceOperatorConfig
metadata:
  name: devworkspace-operator-config
  namespace: openshift-operators
config:
  workspace:
    backupCronJob:
      enable: true
      schedule: "* * * * *"
      registry:
        path: "ttl.sh/backups"
        # authSecret is NOT set - registry allows anonymous access
EOF
  1. Create test namespace
oc new-project test-workspace-public-registry
  1. Create a DevWorkspace
kubectl apply -f - <<EOF
apiVersion: workspace.devfile.io/v1alpha2
kind: DevWorkspace
metadata:
  name: test-workspace-public
  namespace: test-workspace-public-registry
spec:
  started: true
  template:
    projects:
      - name: web-nodejs-sample
        git:
          remotes:
            origin: "https://github.com/che-samples/web-nodejs-sample.git"
    components:
      - name: dev
        container:
          image: quay.io/devfile/universal-developer-image:latest
          memoryLimit: 512Mi
EOF
  1. Wait for workspace to become running, then stop it:
kubectl patch devworkspace test-workspace-public -n test-workspace-public-registry \
  --type merge -p '{"spec":{"started":false}}'
  1. Expected Result: Backup job created without auth:
kubectl get jobs -n test-workspace-public-registry -l controller.devfile.io/devworkspace_name=test-workspace-public

PR Checklist

  • E2E tests pass (when PR is ready, comment /test v8-devworkspace-operator-e2e, v8-che-happy-path to trigger)
    • v8-devworkspace-operator-e2e: DevWorkspace e2e test
    • v8-che-happy-path: Happy path for verification integration with Che

Summary by CodeRabbit

  • Bug Fixes

    • Registry auth secret handling now prefers existing workspace secrets, falls back to configured operator secret if needed, and enforces "never overwrite" semantics to avoid unintended replacements.
    • Improved error behavior when configured operator secrets are missing.
  • Tests

    • Added tests covering backup-path scenarios: workspace-first retrieval, operator fallback, non-overwrite preservation, and not-found error cases.
  • Style

    • Adjusted backup controller logging naming for clearer logs.

Review Change Stack

@openshift-ci
Copy link
Copy Markdown

openshift-ci Bot commented May 5, 2026

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 5, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR refactors registry auth secret handling in the backup path. HandleRegistryAuthSecret now uses a fixed constant name to look up secrets in the workspace namespace, returns nil when missing and no source is configured, and fetches from the operator namespace when configured. CopySecret is reimplemented with direct Kubernetes Create calls that enforce never-overwrite semantics by re-fetching existing secrets on AlreadyExists. Tests and backup-related controller tests are added/updated; logger verbosity was reduced.

Changes

Registry Auth Secret Backup-Path Refactoring

Layer / File(s) Summary
Import update
pkg/secrets/backup.go
Added Kubernetes API error helpers; removed sync package dependency.
Secret lookup and error handling
pkg/secrets/backup.go
HandleRegistryAuthSecret uses fixed constants.DevWorkspaceBackupAuthSecretName for workspace lookup, returns nil when missing and no operator source configured, validates AuthSecret when falling back, and fetches from operator namespace before copying.
Create-based copy semantics
pkg/secrets/backup.go
CopySecret short-circuits on existing workspace secret, attempts Create to copy operator secret into workspace, re-fetches on AlreadyExists, and removes sync-based reconciliation.
Backup-path tests
pkg/secrets/backup_test.go
New Ginkgo suite for backup path: verify existing workspace secret preserved, empty-config returns nil, operator-to-workspace copy works, never-overwrite behavior, and operator-missing returns NotFound.
Controller test updates
controllers/backupcronjob/backupcronjob_controller_test.go
Tests updated to include AuthSecret in registry config and to create auth Secret in operator/workspace namespaces as needed; local-auth test uses canonical workspace secret name.
Logger tweak
main.go
Removed .V(1) verbosity modifier from BackupCronJobReconciler logger initialization.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • devfile/devworkspace-operator#1614: Modifies the same HandleRegistryAuthSecret function and related tests; introduces restore-path fallback to predefined backup secret name alongside this PR's backup-path adjustments.

Suggested reviewers

  • ibuziuk

Poem

🐰 A secret path, now constant and clear,
No syncs to confuse, no overwrites here,
Create once, then fetch if it's there,
Backup and restore—a careful pair! 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 57.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: refactoring backup/restore secret handling to conditionally copy registry auth secrets from operator to workspace namespace based on configuration.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@openshift-ci
Copy link
Copy Markdown

openshift-ci Bot commented May 5, 2026

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: rohanKanojia
Once this PR has been reviewed and has the lgtm label, please assign dkwon17 for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@rohanKanojia rohanKanojia force-pushed the pr/secret-fallback-option branch from 56c6857 to f6f485c Compare May 5, 2026 11:13
@codecov
Copy link
Copy Markdown

codecov Bot commented May 5, 2026

Codecov Report

❌ Patch coverage is 60.00000% with 22 lines in your changes missing coverage. Please review.
✅ Project coverage is 37.15%. Comparing base (b3e2af5) to head (62cd68c).
⚠️ Report is 33 commits behind head on main.

Files with missing lines Patch % Lines
pkg/secrets/backup.go 61.11% 19 Missing and 2 partials ⚠️
main.go 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1618      +/-   ##
==========================================
+ Coverage   35.48%   37.15%   +1.67%     
==========================================
  Files         168      168              
  Lines       14484    14764     +280     
==========================================
+ Hits         5139     5486     +347     
+ Misses       9006     8923      -83     
- Partials      339      355      +16     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@rohanKanojia rohanKanojia force-pushed the pr/secret-fallback-option branch 3 times, most recently from 802c09c to 87f720c Compare May 5, 2026 15:16
@rohanKanojia rohanKanojia marked this pull request as ready for review May 5, 2026 15:50
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (4)
controllers/backupcronjob/backupcronjob_controller_test.go (1)

312-314: ⚡ Quick win

Add one unset/false-path test for CopyOperatorAuthSecret.

All touched executeBackupSync fixtures now force CopyOperatorAuthSecret=true, so this suite no longer validates the new default/disabled branch. Please add at least one case for unset/false (especially missing workspace secret) to guard the new behavior.

Also applies to: 345-346, 403-404, 440-441, 482-483, 514-516, 551-553, 588-589

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@controllers/backupcronjob/backupcronjob_controller_test.go` around lines 312
- 314, Tests in backupcronjob_controller_test.go currently set
CopyOperatorAuthSecret = pointer.Bool(true) in all executeBackupSync fixtures,
so the default/disabled branch (unset/false) and the missing workspace secret
path are not covered; add at least one test case for executeBackupSync where
CopyOperatorAuthSecret is either nil (unset) or pointer.Bool(false) and the
workspace secret is absent, then assert the controller follows the disabled
behavior (e.g., does not attempt to copy operator auth and returns the expected
status/condition). Update the relevant table/fixtures used by
TestExecuteBackupSync (or similarly named test harness) to include this
false/unset case and verify expected outcomes for missing workspace secret to
guard the new branch.
deploy/bundle/manifests/controller.devfile.io_devworkspaceoperatorconfigs.yaml (1)

222-240: ⚡ Quick win

Set an explicit CRD default for copyOperatorAuthSecret.

Line 239 says the field defaults to false, but the schema currently has no default: false. Adding it makes the API contract enforceable and avoids nil/merge ambiguity when the field is omitted.

Suggested schema tweak
                           copyOperatorAuthSecret:
+                            default: false
                             description: |-
                               CopyOperatorAuthSecret controls whether the operator should copy the authentication
                               secret from the operator namespace to the workspace namespace when it's not found
                               in the workspace namespace.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@deploy/bundle/manifests/controller.devfile.io_devworkspaceoperatorconfigs.yaml`
around lines 222 - 240, The CRD schema for the boolean field
copyOperatorAuthSecret currently documents "Defaults to false" but lacks an
explicit default; update the OpenAPI schema for the field copyOperatorAuthSecret
(the property under the spec schema) to include default: false so the CRD
enforces the default value when the field is omitted, ensuring API
server/merging behavior matches the documented contract.
pkg/secrets/backup.go (2)

101-176: 💤 Low value

Logic and race handling look correct.

EnsureRegistryAuthSecret correctly: (1) never overwrites a user-provided secret, (2) returns a clear, actionable error when copyOperatorAuthSecret is false, (3) handles the Get→Create race via IsAlreadyExists re-fetch, and (4) sets the watch label and controller reference on the created secret.

Two minor observations (no action required, just flagging for awareness):

  • The relevant code snippet from pkg/provision/sync/sync.go shows SyncObjectWithCluster provides diff/update behavior. Bypassing it here is the right call since the explicit goal is to never overwrite — just make sure no future caller expects this function to reconcile drift in the workspace secret (e.g., if the operator's source secret rotates, the workspace copy will not be updated).
  • The error message at lines 125–134 references DevWorkspaceBackupAuthSecretName (the predefined name) for both the "not found" name and the "please create" name. That's consistent with the rest of the function but means the user-facing message will not mention the configured secretName from Registry.AuthSecret, which can be confusing if the admin configured a custom name. Consider including both names in the error.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/secrets/backup.go` around lines 101 - 176, Update the error returned when
copyOperatorAuthSecret is false in EnsureRegistryAuthSecret to include both the
workspace secret name (constants.DevWorkspaceBackupAuthSecretName) and the
configured/operator secret name (use sourceSecret.Name) so users see which
configured auth secret name the operator expects versus the name missing in the
workspace; change the fmt.Errorf in the !copyOperatorAuthSecret branch inside
EnsureRegistryAuthSecret to format and include sourceSecret.Name alongside
constants.DevWorkspaceBackupAuthSecretName.

30-30: ⚡ Quick win

Replace deprecated k8s.io/utils/pointer with k8s.io/utils/ptr.

golangci-lint (SA1019) reports that k8s.io/utils/pointer is deprecated. Use ptr.Deref instead of pointer.BoolDeref.

♻️ Proposed fix
-	"k8s.io/utils/pointer"
+	"k8s.io/utils/ptr"
-	copyOperatorAuthSecret := pointer.BoolDeref(
-		dwOperatorConfig.Workspace.BackupCronJob.Registry.CopyOperatorAuthSecret,
-		false,
-	)
+	copyOperatorAuthSecret := ptr.Deref(
+		dwOperatorConfig.Workspace.BackupCronJob.Registry.CopyOperatorAuthSecret,
+		false,
+	)
k8s.io/utils/ptr Deref function signature
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/secrets/backup.go` at line 30, Replace the deprecated import and calls to
pointer.*: change the import "k8s.io/utils/pointer" to "k8s.io/utils/ptr" and
update call sites using pointer.BoolDeref (and any other pointer.XDeref) to use
ptr.Deref instead, e.g. replace pointer.BoolDeref(someBoolPtr, false) with
ptr.Deref(someBoolPtr, false) in the functions/methods in pkg/secrets/backup.go.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@deploy/deployment/kubernetes/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml`:
- Around line 226-244: The CRD documents that copyOperatorAuthSecret defaults to
false but the schema lacks an enforced default; add a JSON schema default by
setting default: false for the copyOperatorAuthSecret property in the CRD (the
YAML under copyOperatorAuthSecret) so the API server will apply the default. If
the CRD is generated from Go types, add the kubebuilder default marker (e.g., //
+kubebuilder:default=false) to the Go field named CopyOperatorAuthSecret (or
copyOperatorAuthSecret) in the API type, regenerate the CRD manifests, and
verify the generated CRD includes default: false under the
copyOperatorAuthSecret schema.

In `@deploy/deployment/openshift/combined.yaml`:
- Around line 226-244: The CRD OpenAPI schema for the copyOperatorAuthSecret
boolean lacks an explicit default; add "default: false" to the property
definition in the combined.yaml CRD schema so the OpenAPIv3 spec matches the
documented and Go behavior (pointer.BoolDeref(..., false)); also update any CRD
description if needed to note this change is a breaking behavioral change for
users who relied on true.

In
`@deploy/deployment/openshift/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml`:
- Around line 223-224: The sentence about the
"controller.devfile.io/watch-secret=true" label is ambiguous: update the text
that references authSecret to state this label requirement only applies to
operator-managed (copied) secrets when copyOperatorAuthSecret is enabled (i.e.,
the operator-created/copy-source path), and clarify that user-provided/manual
authSecret values do not need that label; reference the fields authSecret and
copyOperatorAuthSecret and explicitly mention "operator-managed/copy-source
secrets" and "user-provided/manual secrets" so readers know which secrets must
have controller.devfile.io/watch-secret=true.

In
`@deploy/templates/crd/bases/controller.devfile.io_devworkspaceoperatorconfigs.yaml`:
- Around line 224-242: The CRD schema for the boolean property
copyOperatorAuthSecret declares "Defaults to false" in the description but lacks
an OpenAPI default; update the CRD schema for the copyOperatorAuthSecret
property by adding default: false alongside its type: boolean (i.e., under the
copyOperatorAuthSecret property in the CRD's schema) so the generated API docs
and schema reflect the described default behavior.

In `@pkg/secrets/backup.go`:
- Around line 57-61: The change introduced a breaking default (currently using
pointer.BoolDeref(..., false)) for
dwOperatorConfig.Workspace.BackupCronJob.Registry.CopyOperatorAuthSecret which
stops auto-copying the operator namespace secret; restore backward-compatible
behavior by changing the default to true in the copyOperatorAuthSecret
initialization (i.e., use pointer.BoolDeref(..., true)) so existing clusters
continue to auto-copy, and if you intentionally want false instead, update
docs/changelog and tests that rely on auto-copy (e.g.,
controllers/backupcronjob/backupcronjob_controller_test.go) to avoid upgrade
surprises.

---

Nitpick comments:
In `@controllers/backupcronjob/backupcronjob_controller_test.go`:
- Around line 312-314: Tests in backupcronjob_controller_test.go currently set
CopyOperatorAuthSecret = pointer.Bool(true) in all executeBackupSync fixtures,
so the default/disabled branch (unset/false) and the missing workspace secret
path are not covered; add at least one test case for executeBackupSync where
CopyOperatorAuthSecret is either nil (unset) or pointer.Bool(false) and the
workspace secret is absent, then assert the controller follows the disabled
behavior (e.g., does not attempt to copy operator auth and returns the expected
status/condition). Update the relevant table/fixtures used by
TestExecuteBackupSync (or similarly named test harness) to include this
false/unset case and verify expected outcomes for missing workspace secret to
guard the new branch.

In
`@deploy/bundle/manifests/controller.devfile.io_devworkspaceoperatorconfigs.yaml`:
- Around line 222-240: The CRD schema for the boolean field
copyOperatorAuthSecret currently documents "Defaults to false" but lacks an
explicit default; update the OpenAPI schema for the field copyOperatorAuthSecret
(the property under the spec schema) to include default: false so the CRD
enforces the default value when the field is omitted, ensuring API
server/merging behavior matches the documented contract.

In `@pkg/secrets/backup.go`:
- Around line 101-176: Update the error returned when copyOperatorAuthSecret is
false in EnsureRegistryAuthSecret to include both the workspace secret name
(constants.DevWorkspaceBackupAuthSecretName) and the configured/operator secret
name (use sourceSecret.Name) so users see which configured auth secret name the
operator expects versus the name missing in the workspace; change the fmt.Errorf
in the !copyOperatorAuthSecret branch inside EnsureRegistryAuthSecret to format
and include sourceSecret.Name alongside
constants.DevWorkspaceBackupAuthSecretName.
- Line 30: Replace the deprecated import and calls to pointer.*: change the
import "k8s.io/utils/pointer" to "k8s.io/utils/ptr" and update call sites using
pointer.BoolDeref (and any other pointer.XDeref) to use ptr.Deref instead, e.g.
replace pointer.BoolDeref(someBoolPtr, false) with ptr.Deref(someBoolPtr, false)
in the functions/methods in pkg/secrets/backup.go.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 58f3b582-37ca-446d-9ace-6bf37dec79a5

📥 Commits

Reviewing files that changed from the base of the PR and between fa371eb and 87f720c.

📒 Files selected for processing (12)
  • apis/controller/v1alpha1/devworkspaceoperatorconfig_types.go
  • apis/controller/v1alpha1/zz_generated.deepcopy.go
  • controllers/backupcronjob/backupcronjob_controller_test.go
  • deploy/bundle/manifests/controller.devfile.io_devworkspaceoperatorconfigs.yaml
  • deploy/deployment/kubernetes/combined.yaml
  • deploy/deployment/kubernetes/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml
  • deploy/deployment/openshift/combined.yaml
  • deploy/deployment/openshift/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml
  • deploy/templates/crd/bases/controller.devfile.io_devworkspaceoperatorconfigs.yaml
  • pkg/config/sync.go
  • pkg/secrets/backup.go
  • pkg/secrets/backup_test.go

Comment thread deploy/deployment/openshift/combined.yaml Outdated
Comment on lines 223 to 224
The secret must contain "controller.devfile.io/watch-secret=true" label so that it can be
recognized by the operator.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Clarify label requirement scope for user-provided secrets

Line 223 currently reads as if all authSecret values must have controller.devfile.io/watch-secret=true. That conflicts with the manual-secret path when copyOperatorAuthSecret is disabled. Please scope this sentence to operator-managed/copy-source secrets only.

✏️ Proposed wording tweak
- The secret must contain "controller.devfile.io/watch-secret=true" label so that it can be
- recognized by the operator.
+ For operator-managed secret copying, the source secret in the operator namespace must contain
+ "controller.devfile.io/watch-secret=true" so the operator can recognize and manage it.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
The secret must contain "controller.devfile.io/watch-secret=true" label so that it can be
recognized by the operator.
For operator-managed secret copying, the source secret in the operator namespace must contain
"controller.devfile.io/watch-secret=true" so the operator can recognize and manage it.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@deploy/deployment/openshift/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml`
around lines 223 - 224, The sentence about the
"controller.devfile.io/watch-secret=true" label is ambiguous: update the text
that references authSecret to state this label requirement only applies to
operator-managed (copied) secrets when copyOperatorAuthSecret is enabled (i.e.,
the operator-created/copy-source path), and clarify that user-provided/manual
authSecret values do not need that label; reference the fields authSecret and
copyOperatorAuthSecret and explicitly mention "operator-managed/copy-source
secrets" and "user-provided/manual secrets" so readers know which secrets must
have controller.devfile.io/watch-secret=true.

Comment thread deploy/templates/crd/bases/controller.devfile.io_devworkspaceoperatorconfigs.yaml Outdated
Comment thread pkg/secrets/backup.go Outdated
@rohanKanojia rohanKanojia force-pushed the pr/secret-fallback-option branch from 87f720c to 0f8ddda Compare May 5, 2026 16:47
Comment on lines +93 to +95
// If true: The operator will copy the secret from the operator namespace
// if it's not found in the workspace namespace. This provides automatic configuration
// but exposes operator-level credentials to workspace users.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's a bit verbose,

Can we update thee If true... section to:

If true, this enables the fallback mechanism where the operator will copy the secret from the operator namespace.

//
// If false (default): The operator will not copy the secret. Users must manually create a secret
// with the configured name in their workspace namespace. This is more secure as it allows
// users to provide scoped credentials with minimal privileges.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's a bit verbose,

Can we update the If false... section to:

If false, the operator will not copy the secret to workspace namespaces. 

// users to provide scoped credentials with minimal privileges.
//
// Note: Regardless of this setting, if a secret already exists in the workspace namespace,
// it will never be overwritten. User-provided secrets are always respected.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// it will never be overwritten. User-provided secrets are always respected.
// it will never be overwritten.

Comment thread pkg/secrets/backup.go Outdated
// Extract flag value (default: false, users must provide their own secret)
copyOperatorAuthSecret := pointer.BoolDeref(
dwOperatorConfig.Workspace.BackupCronJob.Registry.CopyOperatorAuthSecret,
false,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we instead define the default field here?

pkg/config/defaults.go

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initially, I also thought about it, but then hesitated as RegistryConfig wasn't defined in defaults.go as Registry.Path is a required value with no sensible default.

If we add configuration like this for the default value:

  BackupCronJob: &v1alpha1.BackupCronJobConfig{
      Enable:       pointer.Bool(false),
      Schedule:     "0 0 1 * *",
      BackoffLimit: pointer.Int32(1),
      Registry: &v1alpha1.RegistryConfig{
          Path:                   "", // Required but no default
          AuthSecret:             "",
          CopyOperatorAuthSecret: pointer.Bool(false),
      },
  },

But this would generate an incomplete/invalid configuration.

Do you think I should proceed with this approach? Or create a named constant for false value in the current line?

Comment thread pkg/secrets/backup.go Outdated
return nil, fmt.Errorf(
"registry auth secret %q not found in workspace namespace %q. "+
"copyOperatorAuthSecret is set to false, so the operator will not copy the secret. "+
"Please manually create a secret of type kubernetes.io/dockerconfigjson with the name %q "+
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"Please manually create a secret of type kubernetes.io/dockerconfigjson with the name %q "+
"Please create a secret of type kubernetes.io/dockerconfigjson with the name %q "+

Comment thread pkg/secrets/backup.go Outdated
if !copyOperatorAuthSecret {
return nil, fmt.Errorf(
"registry auth secret %q not found in workspace namespace %q. "+
"copyOperatorAuthSecret is set to false, so the operator will not copy the secret. "+
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"copyOperatorAuthSecret is set to false, so the operator will not copy the secret. "+
"copyOperatorAuthSecret is set to false, the secret will not be copied. "+

@dkwon17
Copy link
Copy Markdown
Collaborator

dkwon17 commented May 6, 2026

@rohanKanojia on second thought, is there ever a use case where the admin sets an authSecret and sets copyOperatorAuthSecret to false?

config:
  workspace:
    backupCronJob:
      enable: true
      schedule: "* * * * *"
      registry:
        path: "quay.io/rokumar"
        authSecret: "quay-push-secret"
        copyOperatorAuthSecret: false

Because, the only purpose of the admin supplying the authSecret is so that it can be used in user namespace, but in the case above, copyOperatorAuthSecret will prevent that.

Also in general the secret name provided inauthSecret name is now not important, since when checking/retrieving the pull secret from the user namespace, the code always checks for a secret named: devworkspace-backup-registry-auth(value of constants.DevWorkspaceBackupAuthSecretName)

@rohanKanojia rohanKanojia force-pushed the pr/secret-fallback-option branch from 0f8ddda to 43f4862 Compare May 7, 2026 04:28
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/secrets/backup.go (1)

63-98: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

AuthSecret name is silently ignored on the workspace-namespace fallback path — confirm UX.

The function uses two different names depending on the path:

  • Backup path (line 66, lookupName = secretName): the workspace namespace is searched under the operator-configured AuthSecret name.
  • Restore path (line 68) and the EnsureRegistryAuthSecret write path (line 108): the predefined constants.DevWorkspaceBackupAuthSecretName is used.

So when an admin sets authSecret: my-secret and copyOperatorAuthSecret: false, and the workspace user has not pre-created any secret, the workspace lookup at line 71–79 will succeed only if they created my-secret, but EnsureRegistryAuthSecret (and the error it returns at lines 125–134) instructs them to create devworkspace-backup-registry-auth. The two paths disagree on the expected name.

Please align the names — either always look for/instruct on constants.DevWorkspaceBackupAuthSecretName, or always honor the operator-configured AuthSecret name in both lookup and the user-facing error. This was also raised in PR review feedback by dkwon17.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/secrets/backup.go` around lines 63 - 98, The code currently looks up the
workspace secret using secretName but EnsureRegistryAuthSecret (and its
error/log messages) uses constants.DevWorkspaceBackupAuthSecretName, causing a
mismatch; fix by making the operator-configured AuthSecret name authoritative:
update EnsureRegistryAuthSecret (and any user-facing error/log text) to use the
passed-in secretName instead of constants.DevWorkspaceBackupAuthSecretName so
the lookupName logic, the Get calls, and the EnsureRegistryAuthSecret
write/error messaging all reference the same secretName; ensure references to
lookupName, secretName, and EnsureRegistryAuthSecret are consistent and adjust
tests/logs accordingly.
♻️ Duplicate comments (1)
deploy/deployment/openshift/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml (1)

221-224: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Scope the watch-secret label requirement to operator-managed copy-source secrets only.

The label sentence is still ambiguous and reads as if it applies to all authSecret values. Please explicitly distinguish operator-managed/copy-source secrets vs user-provided/manual workspace secrets.

✏️ Suggested doc tweak
- If secret is not found in the workspace namespace and copyOperatorAuthSecret is true,
- the operator will copy the secret from the operator namespace to the workspace namespace.
- The secret must contain "controller.devfile.io/watch-secret=true" label so that it can be
- recognized by the operator.
+ If secret is not found in the workspace namespace and copyOperatorAuthSecret is true,
+ the operator will copy the secret from the operator namespace to the workspace namespace.
+ For operator-managed/copy-source secrets, the source secret in the operator namespace must
+ contain "controller.devfile.io/watch-secret=true" so it can be recognized by the operator.
+ User-provided/manual secrets in workspace namespaces do not require this label.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@deploy/deployment/openshift/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml`
around lines 221 - 224, Clarify that the
"controller.devfile.io/watch-secret=true" label is only required for
operator-managed copy-source secrets when copyOperatorAuthSecret is true: update
the description around the authSecret/copyOperatorAuthSecret text to state that
if copyOperatorAuthSecret is true the operator will copy the secret from the
operator namespace and that only those copy-source secrets must carry the
controller.devfile.io/watch-secret=true label; explicitly note that
user-provided/manual workspace secrets (i.e., when users set authSecret directly
in the workspace namespace) do not need this label. Reference
copyOperatorAuthSecret and authSecret and the watch-secret label in the updated
sentence so it's unambiguous.
🧹 Nitpick comments (2)
pkg/secrets/backup.go (2)

87-87: 💤 Low value

Log key/value mismatch on the restore path.

lookupName was used for the workspace-namespace Get (line 73), but this log line reports secretName as the secret it failed to find in the workspace namespace. On the restore path these can differ (line 68 overrides lookupName to the predefined constant), so the log will misattribute the missing key. Use lookupName here.

♻️ Proposed fix
-	log.Info("Registry auth secret not found in workspace namespace, checking operator namespace", "secretName", secretName)
+	log.Info("Registry auth secret not found in workspace namespace, checking operator namespace",
+		"workspaceSecretName", lookupName, "operatorSecretName", secretName)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/secrets/backup.go` at line 87, The log message in pkg/secrets/backup.go
incorrectly reports "secretName" when the code uses lookupName for the
workspace-namespace Get; update the log.Info call that currently logs
"secretName" to instead log the lookupName variable so the message reflects the
actual attempted key (refer to the lookupName variable and the log.Info call
that says "Registry auth secret not found in workspace namespace, checking
operator namespace").

27-30: 💤 Low value

Switch from deprecated k8s.io/utils/pointer to k8s.io/utils/ptr.

The pointer package is deprecated in favor of ptr. Use ptr.Deref (replacing pointer.BoolDeref) to align with Kubernetes project migration guidance.

♻️ Proposed refactor
-	"k8s.io/utils/pointer"
+	"k8s.io/utils/ptr"
-	copyOperatorAuthSecret := pointer.BoolDeref(
-		dwOperatorConfig.Workspace.BackupCronJob.Registry.CopyOperatorAuthSecret,
-		false,
-	)
+	copyOperatorAuthSecret := ptr.Deref(
+		dwOperatorConfig.Workspace.BackupCronJob.Registry.CopyOperatorAuthSecret,
+		false,
+	)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/secrets/backup.go` around lines 27 - 30, The code imports the deprecated
package alias pointer and uses pointer.BoolDeref; replace the import
"k8s.io/utils/pointer" with "k8s.io/utils/ptr" (alias it as ptr if you used
pointer) and update usages of pointer.BoolDeref to ptr.Deref (e.g.,
pointer.BoolDeref(someBoolPtr, false) -> ptr.Deref(someBoolPtr, false)); also
change any other pointer.* helpers to their ptr.* equivalents (e.g.,
pointer.Bool -> ptr.Bool) so the file uses the non-deprecated ptr package and
ptr.Deref consistently.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/secrets/backup.go`:
- Around line 125-134: The user-facing error string in the fmt.Errorf return
(the block referencing constants.DevWorkspaceBackupAuthSecretName and
workspace.Namespace in the copyOperatorAuthSecret branch) concatenates sentences
without a space resulting in "...will not be copied.Please create..."; fix it by
inserting a space between the period and "Please" (or restructure the string
concatenation so sentences are separated with a space) so the message reads
"...will not be copied. Please create...".

---

Outside diff comments:
In `@pkg/secrets/backup.go`:
- Around line 63-98: The code currently looks up the workspace secret using
secretName but EnsureRegistryAuthSecret (and its error/log messages) uses
constants.DevWorkspaceBackupAuthSecretName, causing a mismatch; fix by making
the operator-configured AuthSecret name authoritative: update
EnsureRegistryAuthSecret (and any user-facing error/log text) to use the
passed-in secretName instead of constants.DevWorkspaceBackupAuthSecretName so
the lookupName logic, the Get calls, and the EnsureRegistryAuthSecret
write/error messaging all reference the same secretName; ensure references to
lookupName, secretName, and EnsureRegistryAuthSecret are consistent and adjust
tests/logs accordingly.

---

Duplicate comments:
In
`@deploy/deployment/openshift/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml`:
- Around line 221-224: Clarify that the
"controller.devfile.io/watch-secret=true" label is only required for
operator-managed copy-source secrets when copyOperatorAuthSecret is true: update
the description around the authSecret/copyOperatorAuthSecret text to state that
if copyOperatorAuthSecret is true the operator will copy the secret from the
operator namespace and that only those copy-source secrets must carry the
controller.devfile.io/watch-secret=true label; explicitly note that
user-provided/manual workspace secrets (i.e., when users set authSecret directly
in the workspace namespace) do not need this label. Reference
copyOperatorAuthSecret and authSecret and the watch-secret label in the updated
sentence so it's unambiguous.

---

Nitpick comments:
In `@pkg/secrets/backup.go`:
- Line 87: The log message in pkg/secrets/backup.go incorrectly reports
"secretName" when the code uses lookupName for the workspace-namespace Get;
update the log.Info call that currently logs "secretName" to instead log the
lookupName variable so the message reflects the actual attempted key (refer to
the lookupName variable and the log.Info call that says "Registry auth secret
not found in workspace namespace, checking operator namespace").
- Around line 27-30: The code imports the deprecated package alias pointer and
uses pointer.BoolDeref; replace the import "k8s.io/utils/pointer" with
"k8s.io/utils/ptr" (alias it as ptr if you used pointer) and update usages of
pointer.BoolDeref to ptr.Deref (e.g., pointer.BoolDeref(someBoolPtr, false) ->
ptr.Deref(someBoolPtr, false)); also change any other pointer.* helpers to their
ptr.* equivalents (e.g., pointer.Bool -> ptr.Bool) so the file uses the
non-deprecated ptr package and ptr.Deref consistently.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a377f982-ce2d-4827-926d-c2adb24e358e

📥 Commits

Reviewing files that changed from the base of the PR and between 87f720c and 43f4862.

📒 Files selected for processing (12)
  • apis/controller/v1alpha1/devworkspaceoperatorconfig_types.go
  • apis/controller/v1alpha1/zz_generated.deepcopy.go
  • controllers/backupcronjob/backupcronjob_controller_test.go
  • deploy/bundle/manifests/controller.devfile.io_devworkspaceoperatorconfigs.yaml
  • deploy/deployment/kubernetes/combined.yaml
  • deploy/deployment/kubernetes/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml
  • deploy/deployment/openshift/combined.yaml
  • deploy/deployment/openshift/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml
  • deploy/templates/crd/bases/controller.devfile.io_devworkspaceoperatorconfigs.yaml
  • pkg/config/sync.go
  • pkg/secrets/backup.go
  • pkg/secrets/backup_test.go
✅ Files skipped from review due to trivial changes (1)
  • deploy/deployment/kubernetes/combined.yaml
🚧 Files skipped from review as they are similar to previous changes (8)
  • pkg/config/sync.go
  • apis/controller/v1alpha1/devworkspaceoperatorconfig_types.go
  • deploy/deployment/kubernetes/objects/devworkspaceoperatorconfigs.controller.devfile.io.CustomResourceDefinition.yaml
  • deploy/deployment/openshift/combined.yaml
  • deploy/templates/crd/bases/controller.devfile.io_devworkspaceoperatorconfigs.yaml
  • pkg/secrets/backup_test.go
  • deploy/bundle/manifests/controller.devfile.io_devworkspaceoperatorconfigs.yaml
  • apis/controller/v1alpha1/zz_generated.deepcopy.go

Comment thread pkg/secrets/backup.go Outdated
@rohanKanojia rohanKanojia force-pushed the pr/secret-fallback-option branch from 18bf441 to 385a1b3 Compare May 7, 2026 09:40
@openshift-ci
Copy link
Copy Markdown

openshift-ci Bot commented May 7, 2026

@rohanKanojia: The following test failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/v14-devworkspace-operator-e2e 385a1b3 link true /test v14-devworkspace-operator-e2e

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

@rohanKanojia rohanKanojia force-pushed the pr/secret-fallback-option branch from 385a1b3 to 8afc9dc Compare May 8, 2026 11:33
@rohanKanojia rohanKanojia requested a review from btjd as a code owner May 8, 2026 11:33
@rohanKanojia rohanKanojia marked this pull request as draft May 8, 2026 11:34
@rohanKanojia rohanKanojia changed the title feat (backup) : add option to conditionally copy registry auth secret from operator namespace to workspace namespace for backup/restore refactor (backup) : conditionally copy registry auth secret from operator namespace to workspace namespace for backup/restore May 8, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
pkg/secrets/backup.go (2)

34-40: ⚡ Quick win

Stale doc comment references the old function name.

The godoc on line 34 still says GetRegistryAuthSecret while the function is now GetNamespaceRegistryAuthSecret. Please align the comment with the new name (and ideally describe what "Namespace" means here since this thin wrapper just calls HandleRegistryAuthSecret with an empty operatorConfigNamespace).

📝 Suggested doc fix
-// GetRegistryAuthSecret retrieves the registry authentication secret for accessing backup images
-// based on the operator configuration.
+// GetNamespaceRegistryAuthSecret retrieves the registry authentication secret from the workspace
+// namespace only (no fallback to the operator namespace). It is intended for the restore path where
+// only user-provided, workspace-scoped credentials should be used.
 func GetNamespaceRegistryAuthSecret(ctx context.Context, c client.Client, workspace *dw.DevWorkspace,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/secrets/backup.go` around lines 34 - 40, Update the stale godoc for
GetNamespaceRegistryAuthSecret to reference its current name (replace
"GetRegistryAuthSecret" with "GetNamespaceRegistryAuthSecret") and clarify that
this wrapper calls HandleRegistryAuthSecret with an empty
operatorConfigNamespace to use the default/operator-scoped config; mention what
"Namespace" refers to (i.e., it operates for the workspace namespace by passing
an empty operatorConfigNamespace). Ensure the comment sits immediately above the
GetNamespaceRegistryAuthSecret function signature.

94-152: ⚡ Quick win

CopySecret is now narrowly purpose-built for the backup-auth secret; consider renaming/narrowing the signature.

The function still has the generic name CopySecret(... sourceSecret *corev1.Secret ...), but it ignores sourceSecret.Name/sourceSecret.Namespace and hardcodes the destination name to constants.DevWorkspaceBackupAuthSecretName. A future caller passing sourceSecret named "foo" would silently end up with a workspace secret named devworkspace-backup-registry-auth, which is surprising.

Two reasonable options:

  1. Rename to something like EnsureBackupAuthSecretInWorkspace and shrink the signature to just the bits actually consumed (data map[string][]byte, secretType corev1.SecretType), making it explicit that this is not a general-purpose copy.
  2. Keep the generic name but actually honor sourceSecret.Name (and require it to equal the constant when called from HandleRegistryAuthSecret), so the function stays reusable.

Also note: this nicely pairs with the previous comment — if Registry.AuthSecret becomes the operator-namespace source name, option (1) becomes the cleaner separation, since the source/destination naming differ.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/secrets/backup.go` around lines 94 - 152, The CopySecret function is
actually specialized for the backup-auth secret but still takes a generic
sourceSecret and ignores its name; rename CopySecret to
EnsureBackupAuthSecretInWorkspace (or similar) and shrink its signature to only
accept the fields it uses (e.g., data map[string][]byte, secretType
corev1.SecretType, workspace *dw.DevWorkspace, scheme *runtime.Scheme,
ctx/client/log) so callers can't accidentally expect a generic copy; update all
callers (e.g., HandleRegistryAuthSecret) to pass sourceSecret.Data and
sourceSecret.Type (or keep a thin adapter if needed), keep the hardcoded
destination name constants.DevWorkspaceBackupAuthSecretName and the existing
controllerutil.SetControllerReference, and preserve the existing
Get/Create/IsAlreadyExists race handling and logging.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/secrets/backup.go`:
- Around line 67-89: The code currently ignores the admin-configured name in
dwOperatorConfig.Workspace.BackupCronJob.Registry.AuthSecret and always looks up
constants.DevWorkspaceBackupAuthSecretName in the operator namespace; change the
operator-namespace lookup (the c.Get call that populates registryAuthSecret) to
use the configured name
(dwOperatorConfig.Workspace.BackupCronJob.Registry.AuthSecret) instead of the
constant, and update the preceding error/log message to reflect that the
configured name will be used for the operator-namespace lookup; also update
related tests (backup_test.go) to seed the operator namespace with the
configured secret name rather than constants.DevWorkspaceBackupAuthSecretName.

---

Nitpick comments:
In `@pkg/secrets/backup.go`:
- Around line 34-40: Update the stale godoc for GetNamespaceRegistryAuthSecret
to reference its current name (replace "GetRegistryAuthSecret" with
"GetNamespaceRegistryAuthSecret") and clarify that this wrapper calls
HandleRegistryAuthSecret with an empty operatorConfigNamespace to use the
default/operator-scoped config; mention what "Namespace" refers to (i.e., it
operates for the workspace namespace by passing an empty
operatorConfigNamespace). Ensure the comment sits immediately above the
GetNamespaceRegistryAuthSecret function signature.
- Around line 94-152: The CopySecret function is actually specialized for the
backup-auth secret but still takes a generic sourceSecret and ignores its name;
rename CopySecret to EnsureBackupAuthSecretInWorkspace (or similar) and shrink
its signature to only accept the fields it uses (e.g., data map[string][]byte,
secretType corev1.SecretType, workspace *dw.DevWorkspace, scheme
*runtime.Scheme, ctx/client/log) so callers can't accidentally expect a generic
copy; update all callers (e.g., HandleRegistryAuthSecret) to pass
sourceSecret.Data and sourceSecret.Type (or keep a thin adapter if needed), keep
the hardcoded destination name constants.DevWorkspaceBackupAuthSecretName and
the existing controllerutil.SetControllerReference, and preserve the existing
Get/Create/IsAlreadyExists race handling and logging.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 48f686d6-3831-4b20-8a0e-357bc50c82c8

📥 Commits

Reviewing files that changed from the base of the PR and between 385a1b3 and 8afc9dc.

📒 Files selected for processing (2)
  • pkg/secrets/backup.go
  • pkg/secrets/backup_test.go

Comment thread pkg/secrets/backup.go
@tolusha
Copy link
Copy Markdown
Contributor

tolusha commented May 8, 2026

Hi! I'm che-ai-assistant — I help with your pull requests.

I check for new commands every 5m0s (if I am not busy :) ).

Available commands:

  • /che-ai-assistant generate-che-doc — Generate a documentation PR based on this PR's changes
  • /che-ai-assistant ok-pr-review — Run a comprehensive PR review (summary, code review, deep review, impact analysis)
  • /che-ai-assistant help — Show this help message

…ator namespace to workspace namespace for backup/restore

Only copy the registry auth secret from the operator namespace to the workspace namespace when it is configured inside DevWorkspaceOperatorConfig. Always give precedence to the user-defined secret named devworkspace-backup-registry-auth in the workspace namespace; do not modify it when it's present while copying.

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>
Signed-off-by: Rohan Kumar <[email protected]>
@dkwon17
Copy link
Copy Markdown
Collaborator

dkwon17 commented May 9, 2026

For scenario 1, if no auth secret is created, the backup cron job pod should still start (this is the use case when using the internal openshift registry, or, a registry where no auth is required)

Right now I am getting this error:

"registry auth secret 
  \"devworkspace-backup-registry-auth\" not found in workspace namespace 
  \"test-workspace-no-auth\" and AuthSecret is not configured in DevWorkspaceOperatorConfig.
   Either create the secret in the workspace namespace or configure Registry.AuthSecret in 
  the operator config to enable copying from the operator namespace"

@rohanKanojia
Copy link
Copy Markdown
Member Author

@dkwon17 : Do you mean to say in this case we should just log a message instead of throwing an error?

The BackupCronJob controller initialized its root logger with V(1),
making all controller logs hidden unless the operator was started with
increased zap verbosity.

Remove the verbosity override from the controller logger to align with
other controllers and use default log visibility consistently.

Signed-off-by: Rohan Kumar <[email protected]>
@rohanKanojia
Copy link
Copy Markdown
Member Author

@dkwon17 : I've modified code to just log a message instead of throwing error. Now I can confirm that backup pod gets created for openshift internal registry.

I've added new test scenarios for openshift interal registry and external registry with no auth.

@rohanKanojia rohanKanojia marked this pull request as ready for review May 11, 2026 12:30
@openshift-ci openshift-ci Bot requested a review from dkwon17 May 11, 2026 12:30
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (6)
pkg/secrets/backup.go (3)

98-156: 💤 Low value

Refactor looks good; consider documenting the naming model.

The Get-then-Create flow with IsAlreadyExists race handling correctly enforces never-overwrite semantics, and returning the pre-existing secret without modification matches the contract in the doc comment. Owner reference is set on the freshly created copy only, so user-provided secrets are not implicitly garbage-collected on workspace deletion — which is the safer default.

One follow-up worth surfacing in user-facing docs (CRD comment for RegistryConfig.AuthSecret and/or the godoc on HandleRegistryAuthSecret): the workspace-namespace secret name is always the canonical devworkspace-backup-registry-auth, while AuthSecret only configures the source name in the operator namespace. This mismatch came up in the PR discussion and is easy to miss from just reading the field name.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/secrets/backup.go` around lines 98 - 156, The docs should explicitly
state that the workspace-namespace secret is always named by the operator
constant (constants.DevWorkspaceBackupAuthSecretName /
"devworkspace-backup-registry-auth") and that RegistryConfig.AuthSecret refers
only to the source secret name in the operator namespace; update the CRD field
comment for RegistryConfig.AuthSecret and the godoc for HandleRegistryAuthSecret
(and/or pkg/secrets.CopySecret) to mention this naming model and the
non-overwrite behavior so users are not surprised by the canonical workspace
secret name.

34-40: 💤 Low value

Stale godoc references the prior function name.

The doc comment starts with GetRegistryAuthSecret retrieves ... but the function is now GetNamespaceRegistryAuthSecret. Godoc/golint tooling treats the first word as the documented identifier, so this generates misleading documentation.

✏️ Proposed fix
-// GetRegistryAuthSecret retrieves the registry authentication secret for accessing backup images
-// based on the operator configuration.
+// GetNamespaceRegistryAuthSecret retrieves the registry authentication secret for accessing
+// backup images from the workspace namespace only (no operator-namespace fallback).
 func GetNamespaceRegistryAuthSecret(ctx context.Context, c client.Client, workspace *dw.DevWorkspace,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/secrets/backup.go` around lines 34 - 40, Update the stale godoc so its
opening identifier matches the actual function name: change the comment that
currently begins "GetRegistryAuthSecret retrieves ..." to begin
"GetNamespaceRegistryAuthSecret retrieves ..." (and verify the rest of the
sentence still accurately describes behavior); locate the comment above the
GetNamespaceRegistryAuthSecret function in pkg/secrets/backup.go and update it
accordingly (no other code changes to GetNamespaceRegistryAuthSecret or the call
to HandleRegistryAuthSecret are required).

79-81: 💤 Low value

Log key labels could be clearer about which name lives in which namespace.

The message reads "not found in workspace namespace, checking operator namespace", but the secretName key carries the operator-namespace name (authSecretName). An operator reading this log will likely assume secretName refers to the missing workspace-NS secret. Splitting into two keys removes the ambiguity, especially since the workspace NS always looks up the canonical name regardless of AuthSecret.

✏️ Suggested clarification
-	log.Info("Registry auth secret not found in workspace namespace, checking operator namespace",
-		"secretName", authSecretName,
-		"operatorNamespace", operatorConfigNamespace)
+	log.Info("Registry auth secret not found in workspace namespace, falling back to operator namespace",
+		"workspaceSecretName", constants.DevWorkspaceBackupAuthSecretName,
+		"workspaceNamespace", workspace.Namespace,
+		"operatorSecretName", authSecretName,
+		"operatorNamespace", operatorConfigNamespace)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/secrets/backup.go` around lines 79 - 81, The log is ambiguous about which
secret name lives in which namespace; update the log.Info call to emit separate,
explicit keys so readers can tell the operator-namespace secret name vs. the
workspace namespace context (e.g. add "operatorSecretName" with authSecretName
and "workspaceNamespace" with the workspace namespace variable used by the
lookup). Locate the log.Info invocation (log.Info) and replace the single
"secretName" key with distinct keys referencing authSecretName for the operator
secret and the existing workspace namespace/secret variable used by the
workspace lookup.
pkg/secrets/backup_test.go (2)

191-215: 💤 Low value

Optional: also assert the copied secret carries the expected label and owner reference.

CopySecret is documented (and implemented) to set both DevWorkspaceWatchSecretLabel and a controller reference to the DevWorkspace. Adding those assertions guards against future regressions where the copy succeeds but loses the metadata needed for cache filtering / GC on workspace deletion.

✏️ Suggested additions
 		By("verifying the secret was created in the workspace namespace")
 		copiedSecret := &corev1.Secret{}
 		err = fakeClient.Get(ctx, client.ObjectKey{
 			Name:      constants.DevWorkspaceBackupAuthSecretName,
 			Namespace: workspaceNS,
 		}, copiedSecret)
 		Expect(err).NotTo(HaveOccurred())
 		Expect(copiedSecret.Data["auth"]).To(Equal([]byte("operator-credentials")))
+		Expect(copiedSecret.Labels).To(HaveKeyWithValue(constants.DevWorkspaceWatchSecretLabel, "true"))
+		Expect(copiedSecret.OwnerReferences).To(HaveLen(1))
+		Expect(copiedSecret.OwnerReferences[0].Name).To(Equal(workspace.Name))
+		Expect(copiedSecret.OwnerReferences[0].Kind).To(Equal("DevWorkspace"))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/secrets/backup_test.go` around lines 191 - 215, Update the test that
verifies secret copying (the It block calling secrets.HandleRegistryAuthSecret)
to also assert that the copied secret includes the expected
DevWorkspaceWatchSecretLabel and has a controller ownerReference pointing to the
DevWorkspace; locate the assertions after retrieving copiedSecret and add checks
for copiedSecret.Labels[constants.DevWorkspaceWatchSecretLabel] (or the exact
label key) and that copiedSecret.OwnerReferences contains an entry with
Controller=true and a UID/Name matching the workspace created by makeWorkspace
(this ensures CopySecret set the label and ownerRef correctly).

246-256: 💤 Low value

Optional: assert that no workspace secret was created on the error path.

Currently the test only asserts the returned error is IsNotFound. Adding a Get against the workspace namespace would pin down that the operator does not leave a partially-initialized/empty devworkspace-backup-registry-auth behind when the configured operator secret is missing.

✏️ Suggested addition
 		result, err := secrets.HandleRegistryAuthSecret(ctx, fakeClient, workspace, config, operatorNS, scheme, log)
 		Expect(err).To(HaveOccurred())
 		Expect(result).To(BeNil())
 		Expect(k8sErrors.IsNotFound(err)).To(BeTrue(), "Should return a NotFound error when secret doesn't exist in operator namespace")
+
+		By("verifying no secret was created in the workspace namespace")
+		leftover := &corev1.Secret{}
+		getErr := fakeClient.Get(ctx, client.ObjectKey{
+			Name:      constants.DevWorkspaceBackupAuthSecretName,
+			Namespace: workspaceNS,
+		}, leftover)
+		Expect(k8sErrors.IsNotFound(getErr)).To(BeTrue())
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/secrets/backup_test.go` around lines 246 - 256, The test for
HandleRegistryAuthSecret should also assert that no workspace secret was created
on the error path: after calling secrets.HandleRegistryAuthSecret with the fake
client and expecting a NotFound error, attempt to Get the Secret named
constants.DevWorkspaceBackupAuthSecretName (or the hardcoded
"devworkspace-backup-registry-auth" used by makeConfig/makeWorkspace) from the
workspace namespace (workspaceNS) via the fakeClient and assert that the Get
returns a NotFound error (i.e., no secret exists); this verifies the operator
did not create a partially-initialized workspace secret when the operator
namespace secret is missing.
controllers/backupcronjob/backupcronjob_controller_test.go (1)

538-574: 💤 Low value

Optional: tighten the spec name to reflect what's being validated.

This case primarily verifies that a pre-existing workspace-scoped secret (canonical name) takes precedence and is honored without needing the operator-NS source. A name like "creates a Job for a DevWorkspace stopped with no previous backup when a workspace-scoped auth secret already exists" reads more aligned with the assertion, since AuthSecret: "my-secret" is intentionally never resolved in this path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@controllers/backupcronjob/backupcronjob_controller_test.go` around lines 538
- 574, Update the test description string for the It(...) case that currently
reads "creates a Job for a DevWorkspace stopped with no previous backup and
local auth registry" to a tighter, more accurate name such as "creates a Job for
a DevWorkspace stopped with no previous backup when a workspace-scoped auth
secret already exists" so the spec reflects that the workspace-scoped secret
(the canonical name created via createAuthSecret) is honored and the
OperatorConfig.AuthSecret ("my-secret") is not used in this path; locate the
It(...) block in the backupcronjob_controller_test (the test where dwoc, dw, pvc
and authSecret are created and reconciler.executeBackupSync is asserted) and
change only the string literal describing the test.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@controllers/backupcronjob/backupcronjob_controller_test.go`:
- Around line 538-574: Update the test description string for the It(...) case
that currently reads "creates a Job for a DevWorkspace stopped with no previous
backup and local auth registry" to a tighter, more accurate name such as
"creates a Job for a DevWorkspace stopped with no previous backup when a
workspace-scoped auth secret already exists" so the spec reflects that the
workspace-scoped secret (the canonical name created via createAuthSecret) is
honored and the OperatorConfig.AuthSecret ("my-secret") is not used in this
path; locate the It(...) block in the backupcronjob_controller_test (the test
where dwoc, dw, pvc and authSecret are created and reconciler.executeBackupSync
is asserted) and change only the string literal describing the test.

In `@pkg/secrets/backup_test.go`:
- Around line 191-215: Update the test that verifies secret copying (the It
block calling secrets.HandleRegistryAuthSecret) to also assert that the copied
secret includes the expected DevWorkspaceWatchSecretLabel and has a controller
ownerReference pointing to the DevWorkspace; locate the assertions after
retrieving copiedSecret and add checks for
copiedSecret.Labels[constants.DevWorkspaceWatchSecretLabel] (or the exact label
key) and that copiedSecret.OwnerReferences contains an entry with
Controller=true and a UID/Name matching the workspace created by makeWorkspace
(this ensures CopySecret set the label and ownerRef correctly).
- Around line 246-256: The test for HandleRegistryAuthSecret should also assert
that no workspace secret was created on the error path: after calling
secrets.HandleRegistryAuthSecret with the fake client and expecting a NotFound
error, attempt to Get the Secret named
constants.DevWorkspaceBackupAuthSecretName (or the hardcoded
"devworkspace-backup-registry-auth" used by makeConfig/makeWorkspace) from the
workspace namespace (workspaceNS) via the fakeClient and assert that the Get
returns a NotFound error (i.e., no secret exists); this verifies the operator
did not create a partially-initialized workspace secret when the operator
namespace secret is missing.

In `@pkg/secrets/backup.go`:
- Around line 98-156: The docs should explicitly state that the
workspace-namespace secret is always named by the operator constant
(constants.DevWorkspaceBackupAuthSecretName /
"devworkspace-backup-registry-auth") and that RegistryConfig.AuthSecret refers
only to the source secret name in the operator namespace; update the CRD field
comment for RegistryConfig.AuthSecret and the godoc for HandleRegistryAuthSecret
(and/or pkg/secrets.CopySecret) to mention this naming model and the
non-overwrite behavior so users are not surprised by the canonical workspace
secret name.
- Around line 34-40: Update the stale godoc so its opening identifier matches
the actual function name: change the comment that currently begins
"GetRegistryAuthSecret retrieves ..." to begin "GetNamespaceRegistryAuthSecret
retrieves ..." (and verify the rest of the sentence still accurately describes
behavior); locate the comment above the GetNamespaceRegistryAuthSecret function
in pkg/secrets/backup.go and update it accordingly (no other code changes to
GetNamespaceRegistryAuthSecret or the call to HandleRegistryAuthSecret are
required).
- Around line 79-81: The log is ambiguous about which secret name lives in which
namespace; update the log.Info call to emit separate, explicit keys so readers
can tell the operator-namespace secret name vs. the workspace namespace context
(e.g. add "operatorSecretName" with authSecretName and "workspaceNamespace" with
the workspace namespace variable used by the lookup). Locate the log.Info
invocation (log.Info) and replace the single "secretName" key with distinct keys
referencing authSecretName for the operator secret and the existing workspace
namespace/secret variable used by the workspace lookup.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 26906af0-afec-45be-abdd-2eb1d2d08e45

📥 Commits

Reviewing files that changed from the base of the PR and between 8afc9dc and 62cd68c.

📒 Files selected for processing (4)
  • controllers/backupcronjob/backupcronjob_controller_test.go
  • main.go
  • pkg/secrets/backup.go
  • pkg/secrets/backup_test.go
✅ Files skipped from review due to trivial changes (1)
  • main.go

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants